#include <eXosip2/eXosip.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <winsock2.h>

#include "uac.h"
#include <pthread.h>
#include <osip2/osip_mt.h>
#include "ua_webrtc.h"

#ifdef ANDROID
#include "uac_jni.h"
#else
#define POS()  printf("%s %d\n", __FUNCTION__, __LINE__)
#define POSI(arg)  printf("%s %d %s\n", __FUNCTION__, __LINE__, arg)
#endif

#define FUN_ENTRY LOGI("���뷽��%s  line %d", __FUNCTION__, __LINE__);
#define FUN_EXIT  LOGI("�˳�����%s  line %d", __FUNCTION__, __LINE__);
#define FUN_ERROR LOGI("�쳣�˳�����%s  line %d", __FUNCTION__, __LINE__);
#define MEDIA_INITED (media != NULL)
#define CALLID_INVALID(callid) ((callid) < 0)

#define ASS(arg)
//#define ASS(x) if(!(x)) {LOGE("%s line %d assert failed", __FUNCTION__, __LINE__);assert(x);}

#define MAX_LEN3 8
#define MAX_LEN4 16
#define MAX_LEN5 32
#define MAX_LEN6 64
#define MAX_LEN7 128
#define MAX_LEN8 256
#define MAX_LEN9 512
#define MAX_LEN10 1024

#define CHECK_NULL_PTR(ptr) {\
  if(NULL == ptr)	{\
  	printf("input null pointer");\
  	return -2;\
  }\
}

#define CHECK_NULL_STR(str) {\
  if(0 == strlen(str)) {\
	  printf("input null string");\
	  return -3;\
  }\
}

typedef struct regparam_t {
  int rid;
  int expiry;
  int auth;

  char server[MAX_LEN7];
  char proxy[MAX_LEN7];
  int port;  // server
  char username[MAX_LEN7];
  char password[MAX_LEN5];
  
    int localsipport;
    char localhostip[128]; 

  int state;
} regparam_t;

typedef struct {
    int chn;
    void *trans;

    int  localport;
    int  remoteport;

    //codec��Ϣ
    char localsendcodecname[128];
    int localsendcodecpl;
    char remotecodecname[128];
    int remotecodecpl;
}MediaUnit;

typedef struct {
    void* sipevent;

    int callid;
    int call_dir;
    int call_type;
    int call_state;
    bool isEnding;

    int  remotesipport;
    char remoteip[128];

    MediaUnit voice;
    MediaUnit video;
}CallUnit;

typedef struct {
    int camera_dir;  // back: 0 front: 1 
    int captureId;
    bool bstartedcamera;
}CamInfo;

int openMediaChannel(CallUnit* pcall, int type);
int closeMediaChannel(CallUnit* pcall, int type);

#if defined(WIN32) || defined(_WIN32_WCE)
#define LOG_INFO 0
#define LOG_ERR 0
#define LOG_WARNING 0
#define LOG_DEBUG 0
#elif defined (ANDROID)
#define LOG_INFO ANDROID_LOG_INFO
#define LOG_ERR ANDROID_LOG_ERROR
#define LOG_WARNING ANDROID_LOG_WARNING
#define LOG_DEBUG ANDROID_LOG_DEBUG
#define syslog_wrapper(log_type, ...)  __android_log_print(log_type, SDK_LOG_TAG, __VA_ARGS__)

#elif defined(LOG_PERROR)
/* If we can, we use syslog() to emit the debugging messages to stderr. */
#define syslog_wrapper syslog
#else
#define syslog_wrapper(a, b...) \
  fprintf(stderr, b);           \
  fprintf(stderr, "\n")
#endif

#define SIP_LOCAL_PORT 15070

struct eXosip_t* ctx;

int gcall_id, gdialog_id, gtrans_id, g_rid = 0;
eXosip_event_t* event;

static volatile int keepSipRunning = 1;

regparam_t g_regparam[1] = {0};
char m_strAuth[256] = {0};
//int camera_index = 1;
CamInfo caminfo[1];
webrtc::WEBRTCIMPL *media;
CallUnit callunit[1];

// PicInfo picInfo[2];

#if defined(WIN32)
unsigned int WINAPI sip_recv_thread(void* param)
#else
static void* sip_recv_thread(void* arg)
#endif
{
     //regparam_t *regparam = (regparam_t*)arg;
  void* env = AttachCurrentThread2JavaVM();
  char info[512] = {0};

  keepSipRunning = 1;
  int counter = 0;

  while (keepSipRunning) {
    
    // eXosip_event_t *event;

    counter++;
    /*if (counter % 500 == 0){  // 5 sec
       sprintf(info, "period callback test counter = %d", counter);
       SdkCallback(env, 0, EXOSIP_EVENT_COUNT+1, info);
    }*/

    if (counter % 60000 == 0) {  // 10 minutes
      struct eXosip_stats stats;

      memset(&stats, 0, sizeof(struct eXosip_stats));
      eXosip_lock(ctx);
      eXosip_set_option(ctx, EXOSIP_OPT_GET_STATISTICS, &stats);
      eXosip_unlock(ctx);
      /*syslog_wrapper(
          LOG_INFO,
          "eXosip stats: inmemory=(tr:%i//reg:%i) average=(tr:%f//reg:%f)",
          stats.allocated_transactions, stats.allocated_registrations,
          stats.average_transactions, stats.average_registrations);*/
    }
    if (!(event = eXosip_event_wait(ctx, 0, 1))) {
#ifdef OSIP_MONOTHREAD
      //eXosip_execute(ctx);
#endif
      eXosip_lock(ctx);
      eXosip_automatic_action(ctx);
      eXosip_unlock(ctx);
      osip_usleep(10000);
      continue;
    }
#ifdef OSIP_MONOTHREAD
    //eXosip_execute(ctx);
#endif

    eXosip_lock(ctx);
    eXosip_automatic_action(ctx);
    //eXosip_default_action(ctx, event);

    switch (event->type) {
      case EXOSIP_REGISTRATION_SUCCESS:
          syslog_wrapper(LOG_INFO, "registrered successfully");
            sprintf(info, "%s", "SDK register success");
            //SdkCallback(env, 0, EXOSIP_REGISTRATION_SUCCESS, info);
        // freeswitch ע��ɹ�������ӡ3��
        if (0 == g_regparam->state) {
            g_regparam->state = 1;
        }

        //syslog_wrapper(LOG_INFO, "registrered successfully");
        break;
      case EXOSIP_REGISTRATION_FAILURE:
          syslog_wrapper(LOG_ERR, "registrered failed");
        if (event->response &&
            event->response->status_code == SIP_UNAUTHORIZED) {
          printf("401!\n");
          //eXosip_register_send_register(ctx, g_regparam->rid, nullptr);
          //regparam.auth = 1;
          // 注册收到 401 之后，再次提交授权信息
          //osip_message_t *reg;
          //eXosip_register_build_register(ctx, event->rid, 3600, &reg);//
//取回认证的字符串authorization
          /*{
            osip_authorization_t * auth;
            osip_www_authenticate_t * www_auth;
            char *strAuth=NULL;
            osip_message_get_authorization(reg,0,&auth);
            //osip_www_authorization_to_str(auth,&strAuth);
            //strcpy(m_strAuth,strAuth);//保存认证字符串
            //delete []strAuth;
            strAuth=NULL;
          }*/

        } else {
          //syslog_wrapper(LOG_ERR, "registrered failed");
          sprintf(info, "%s", "SDK register failed");
          //SdkCallback(env, 0, EXOSIP_REGISTRATION_FAILURE, info);
        }

        //�ͻ����յ�401֮��
        // eXosip�����Զ�������server�������ʹ�nonce��register��֮��ע��ɹ�
        break;

      case EXOSIP_CALL_INVITE:

        //eXosip_lock(ctx);
        eXosip_call_send_answer(ctx, event->tid, SIP_RINGING,
                                NULL);  // answer 180ringing
        //eXosip_unlock(ctx);

        printf("a new invite received!\n");

        printf(
            "Received a INVITE msg from %s:%s, UserName is %s, password is "
            "%s\n",
            event->request->req_uri->host, event->request->req_uri->port,
            event->request->req_uri->username,
            event->request->req_uri->password);
        //�õ���Ϣ��,��Ϊ����Ϣ����SDP��ʽ.
        // remote_sdp = eXosip_get_remote_sdp(excontext, event->did);
        // call_id = event->cid;
        // dialog_id = event->did;

        sprintf(info, "{\"callid\":%d}", event->cid);
        SdkCallback(env, 1, EXOSIP_CALL_INVITE, info);
        break;

      case EXOSIP_CALL_PROCEEDING:  //�յ�100 trying��Ϣ����ʾ�������ڴ�����
        printf("proceeding!\n");
        callunit->sipevent = event;
        callunit->callid = event->cid;
        openMediaChannel(callunit, M_AUDIO);
        break;
      case EXOSIP_CALL_RINGING:  //�յ�180
                                 // RingingӦ����ʾ���յ�INVITE�����UAS���������û�����
        printf("ringing!\n");
        printf("call_id is %d,dialog_id is %d \n", event->cid, event->did);
        break;
      case EXOSIP_CALL_ANSWERED:  //�յ�200 OK����ʾ�����Ѿ����ɹ����ܣ��û�Ӧ��
      {
        osip_message_t* ack = NULL;
        printf("ok!connected! call_id is %d,dialog_id is %d \n", event->cid,
               event->did);

        //����ackӦ����Ϣ
        eXosip_call_build_ack(ctx, event->did, &ack);
        eXosip_call_send_ack(ctx, event->did, ack);

        media->webrtc_api_startmedia(callunit->voice.chn, M_SEND, M_AUDIO);
      }
      break;
      case EXOSIP_CALL_MESSAGE_ANSWERED:
        printf("EXOSIP_CALL_MESSAGE_ANSWERED!\n");
        break;
      case EXOSIP_CALL_RELEASED:
        printf("EXOSIP_CALL_RELEASED!\n");
        break;
      case EXOSIP_CALL_CLOSED:  // a BYE was received for this call
        closeMediaChannel(callunit, M_AUDIO); // �����Ͽ�
        printf("the other sid closed!\n");
        break;
      case EXOSIP_CALL_ACK:  // ACK received for 200ok to INVITE
        printf("ACK received!\n");
        break;
      case EXOSIP_CALL_MESSAGE_NEW:
        break;
      case EXOSIP_MESSAGE_NEW: {
        /*osip_message_t* answer;
        int i;
        printf("EXOSIP_MESSAGE_NEW!\n");
        i = eXosip_message_build_answer(ctx, event->tid, 405, &answer);
        if (i != 0) {
          syslog_wrapper(LOG_ERR, "failed to reject %s",
                         event->request->sip_method);
          break;
        }
        i = eXosip_message_send_answer(ctx, event->tid, 405, answer);
        if (i != 0) {
          syslog_wrapper(LOG_ERR, "failed to reject %s",
                         event->request->sip_method);
          break;
        }
        syslog_wrapper(LOG_INFO, "%s rejected with 405",
                       event->request->sip_method);*/
        break;
      }
      case EXOSIP_IN_SUBSCRIPTION_NEW: {
        /*osip_message_t* answer;
        int i;

        i = eXosip_insubscription_build_answer(ctx, event->tid, 405, &answer);
        if (i != 0) {
          syslog_wrapper(LOG_ERR, "failed to reject %s",
                         event->request->sip_method);
          break;
        }
        i = eXosip_insubscription_send_answer(ctx, event->tid, 405, answer);
        if (i != 0) {
          syslog_wrapper(LOG_ERR, "failed to reject %s",
                         event->request->sip_method);
          break;
        }
        syslog_wrapper(LOG_INFO, "%s rejected with 405",
                       event->request->sip_method);*/
        break;
      }
      default:
        syslog_wrapper(
            LOG_DEBUG,
            "recieved unknown eXosip event (type, did, cid) = (%d, %d, %d)",
            event->type, event->did, event->cid);
    }
    eXosip_unlock(ctx);
    eXosip_event_free(event);
  }

  DetachCurrentThreadFromJavaVM();

  return NULL;
}

char localip[128] = {0};

int sip_init() {
  //regparam.port = SIP_LOCAL_PORT;
  // sprintf(regparam);

  ctx = eXosip_malloc();
  int i = eXosip_init(ctx);
  if (i != 0) {
    POSI("Couldn't initialize eXosip!\n");
    return -1;
  } else {
    POSI("eXosip_init successfully!\n");
  }
  
  eXosip_guess_localip(ctx, AF_INET, localip, sizeof(localip));
  LOGI("localip %s", localip);
	//sprintf(firewallip, "%s", "180.168.102.137");

  //��uac�Լ��Ķ˿�15060�������ж˿ڼ���
  i = eXosip_listen_addr(ctx, IPPROTO_UDP, NULL, SIP_LOCAL_PORT, AF_INET, 0);
  if (i != 0) {
    eXosip_quit(ctx);
    osip_free(ctx);
    syslog_wrapper(LOG_ERR, "Couldn't initialize transport layer: %d", i);
    return -1;
  }

  eXosip_set_user_agent(ctx, "COSIP");

//#ifdef OSIP_MT
  /*osip_thread* register_thread = osip_thread_create (20000, sip_recv_thread, NULL);
  if (register_thread == NULL)
  {
    syslog_wrapper (LOG_ERR, "pthread_create failed");
    exit (1);
  }*/
//#endif
  return 0;
}

int sip_exit() {
  eXosip_quit(ctx);
  osip_free(ctx);
  ctx = NULL;
  printf("Exit the setup!\n");
  keepSipRunning = 0;
  return 0;
}

int sip_register(char* server,
                   char* proxy,
                   //int port,
                   char* username,
                   char* password) {
  // char *username = NULL;
  // char *password = NULL;
  char contact[128]  = {0};
  char fromuser[128] = {0};
  char proxyurl[128] = {0};
  char route[128] = {0};

  osip_message_t* reg = NULL;

    //regparam_t regparam;
    memset(&g_regparam, 0, sizeof(g_regparam));

  //int i;
  if (username && password) {
    syslog_wrapper(LOG_INFO, "username: %s password %s", username, password);
    if (eXosip_add_authentication_info(ctx, username, username, password, nullptr ,
                                       nullptr /*realm*/)) {
      syslog_wrapper(LOG_ERR, "eXosip_add_authentication_info failed");
      exit(1);
    }
  }

	sprintf(fromuser, "sip:%s@%s", username, server);
    sprintf(proxyurl, "sip:%s@%s", username, server);

  sprintf(route,"<sip:%s;lr>",proxy);

  //sprintf(contact, "<sip:%s@192.168.1.100:5060;ob>", username);

  strcpy(g_regparam->server, server);
  strcpy(g_regparam->proxy,proxy);
  //g_regparam->port = port;
  strcpy(g_regparam->username,username);
  strcpy(g_regparam->password,password);

  eXosip_lock(ctx);
    g_regparam->rid = eXosip_register_build_initial_register(ctx, fromuser, proxyurl,
                                                        NULL, 3600, &reg);
  // rid = eXosip_register_build_initial_register (ctx,
  // "sip:me@sip.antisip.com", "sip.antisip.com", NULL, 1800, &reg);
  if (g_regparam->rid < 1) {
    syslog_wrapper(LOG_ERR, "eXosip_register_build_initial_register failed %d",
                   g_regparam->rid);
    exit(1);
  }

  //osip_message_set_supported (reg, "100rel");
  //osip_message_set_supported (reg, "path");

  osip_message_set_route(reg,route);
  //osip_message_set_contact(reg,contact);

  int i = eXosip_register_send_register(ctx, g_regparam->rid, reg);
  if (i != 0) {
    syslog_wrapper(LOG_ERR, "eXosip_register_send_register failed");
    exit(1);
  }
  eXosip_unlock(ctx);

  // need osip free for reg? ���˻����
  //osip_message_free(reg);
  //reg = NULL;

  /*pthread_t tha;
  int reta = pthread_create(&tha, NULL, sip_recv_thread,
                            0);  // TODO: here can optimize param
  if (0 != reta) {
    fprintf(stderr, "Couldn't create thread!\n");
  }*/
  osip_thread* register_thread = osip_thread_create (20000, sip_recv_thread, NULL);
  if (register_thread == NULL)
  {
    syslog_wrapper (LOG_ERR, "pthread_create failed");
    exit (1);
  }
  return 0;
}

int sip_unregister() {
  // send star * can remove all
  // rid = eXosip_register_build_initial_register (ctx,
  // "sip:me@sip.antisip.com", "sip.antisip.com", "*", 1800, &reg);
  int i;
  osip_message_t* reg = NULL;

  //eXosip_lock(ctx);
  i = eXosip_register_build_register(ctx, g_rid, 0, &reg);
  if (i < 0) {
    eXosip_unlock(ctx);
    return -1;
  }

  eXosip_register_send_register(ctx, g_rid, reg);
  //eXosip_unlock(ctx);
  
  // need osip free for reg? ���˻����
  //osip_message_free(reg);
  //reg = NULL;

  return 0;
}

char* sdp_setting() {

    //����SDP��ʽ����������a���Զ����ʽ��Ҳ����˵���Դ���Լ�����Ϣ��
  //����ֻ�������У������ʻ���Ϣ
  //���Ǿ������ԣ���ʽvot�ز����٣�ԭ��δ֪��������Э��ջ�ڴ���ʱ��Ҫ����
  /*snprintf(tmp, 4096,
           "v=0\r\n"
           "o=anonymous 0 0 IN IP4 0.0.0.0\r\n"
           "t=1 10\r\n"
           "a=username:rainfish\r\n"
           "a=password:123\r\n");*/

  /*snprintf(tmp, 4096,
      "v=0\r\n"
      "o=- 0 0 IN IP4 %s\r\n"
      "s=conversation\r\n"
      "c=IN IP4 %s\r\n"
      "t=0 0\r\n"
      "m=audio %d RTP/AVP 0 8 101\r\n"
      "a=rtpmap:0 PCMU/8000\r\n"
      "a=rtpmap:8 PCMA/8000\r\n"
      "a=rtpmap:101 telephone-event/8000\r\n"
      "a=fmtp:101 0-11\r\n"

      "m=video 21000 RTP/AVP 102 99\r\n"
                      "a=rtpmap:102 H264/90000\r\n"
                      "a=rtpmap:99 VP8/90000\r\n",
      localip, localip, port);*/
  char localip[128] = {0};
  char audio_port[32] = {"20000"};
  char video_port[32] = {"21000"};

  eXosip_guess_localip(ctx, AF_INET, localip, sizeof(localip));  //��ñ���ip
  LOGI("sdp localip = %s\n", localip);

  sdp_message_t* sdp = NULL;
  char* dest = NULL;

  sdp_message_init(&sdp);  //��ʼ��sdp��

  sdp_message_v_version_set(sdp, osip_strdup("0"));  //���ð汾
  sdp_message_o_origin_set(sdp, osip_strdup("-"), osip_strdup("0"),
                           osip_strdup("0"), osip_strdup("IN"),
                           osip_strdup("IP4"), osip_strdup(localip));
  sdp_message_s_name_set(sdp, osip_strdup("-"));  //����
  //����start
  // time��stoptime��Ҫ��Ϊ0����Ϊ��֪���˻Ự�Ŀ�ʼ�ͽ���ʱ�䣬�����˵��ȣ�scheduling�����Ѷȡ�
  sdp_message_t_time_descr_add(sdp, osip_strdup("0"), osip_strdup("0"));  //ʱ��
  sdp_message_c_connection_add(sdp, -1, osip_strdup("IN"), osip_strdup("IP4"),
                               osip_strdup(localip), NULL, NULL);  //��������

  // media audio
  sdp_message_m_media_add(sdp, osip_strdup("audio"), osip_strdup(audio_port),
                          NULL, osip_strdup("RTP/AVP"));  //��Ƶ
  sdp_message_m_payload_add(sdp, 0, osip_strdup("8 0 101"));
  sdp_message_a_attribute_add(sdp, 0, osip_strdup("rtpmap"),
                              osip_strdup("8 PCMA/8000"));
  sdp_message_a_attribute_add(sdp, 0, osip_strdup("rtpmap"),
                              osip_strdup("0 PCMU/8000"));
  sdp_message_a_attribute_add(sdp, 0, osip_strdup("rtpmap"),
                              osip_strdup("101 telephone-event/8000"));
  sdp_message_a_attribute_add(sdp, 0, osip_strdup("fmtp"),
                              osip_strdup("101 0-15"));

  // media video
  sdp_message_m_media_add(sdp, osip_strdup("video"), osip_strdup(video_port),
                          NULL, osip_strdup("RTP/AVP"));  //��Ƶ
  sdp_message_m_payload_add(sdp, 1, osip_strdup("96 125"));
  sdp_message_a_attribute_add(sdp, 1, osip_strdup("rtpmap"),
                              osip_strdup("96 VP8/90000"));
  sdp_message_a_attribute_add(sdp, 1, osip_strdup("rtcp-mux"), NULL);

  sdp_message_a_attribute_add(sdp, 1, osip_strdup("rtpmap"),
                              osip_strdup("125 H264/90000"));
  sdp_message_a_attribute_add(
      sdp, 1, osip_strdup("fmtp"),
      osip_strdup("profile-level-id=420D0D;packetization-mode=1"));
  // a=fmtp:125
  // level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
  // sdp_message_a_attribute_add(sdp,1, osip_strdup("rtcp-fb"), osip_strdup("*
  // nack"));

  // sdp_message_m_payload_add(sdp,0,osip_strdup("18"));
  // sdp_message_a_attribute_add(sdp,0,osip_strdup("rtpmap"),osip_strdup("18
  // G729/8000/2"));
  // sdp_message_a_attribute_add(sdp,0,osip_strdup("sendrecv"), NULL);

  sdp_message_to_str(sdp, &dest);  //��sdpת��Ϊ�ַ���

  printf("before sdp setting ok \n");

  sdp_message_free(sdp);  //�ͷ�sdp��
  sdp = NULL;

  printf("sdp setting ok \n");
  return dest;
}

int sip_invite(char* callee) {
  char source_call[MAX_LEN7] = {0}; //(char*)"sip:140@127.0.0.1";
  char dest_call[MAX_LEN7] = {0};   //(char*)"sip:133@127.0.0.1:15061";
  char tmp[4096];
  int port = 20000;
  char localip[MAX_LEN7] = {0};
  osip_message_t* invite;
 
  LOGI("callee is %s", callee);
  sprintf(source_call, "sip:%s@%s", g_regparam->username, g_regparam->server);
  sprintf(dest_call, "sip:%s@%s:%d", callee, g_regparam->server, g_regparam->port);  // ע������ǶԷ�SIP�˿�
  POS();
  int i =
      eXosip_call_build_initial_invite(ctx, &invite, dest_call, source_call,
                                       NULL, "This is a call for conversation");
  if (i != 0) {
    printf("Initial INVITE failed!\n");
    return -1;
  }
  POS();
  osip_message_set_supported(invite, "100rel");
  eXosip_guess_localip(ctx, AF_INET, localip, sizeof(localip));
  
  char* tmpsdp = sdp_setting();

  osip_message_set_body(invite, tmpsdp, strlen(tmpsdp));
  osip_message_set_content_type(invite, "application/sdp");
  POS();
  osip_free(tmpsdp);
  tmpsdp = NULL;
  POS();
  eXosip_lock(ctx);
  i = eXosip_call_send_initial_invite(
      ctx, invite);  // invite SIP INVITE message to send
  if (i > 0) {
    // eXosip_call_set_reference(ctx, i, reference);
  }
  eXosip_unlock(ctx);
  POS();
  //osip_message_free(invite);  // need or not?
  POS();
  return i;
}

int sip_answer(int call_id) {
  osip_message_t* answer = NULL;
  int i;
  char tmp[4096];
  int tid = -1;

  if (event->cid == call_id) {
    tid = event->tid;
  }

  eXosip_lock(ctx);

  openMediaChannel(callunit, M_AUDIO);
  media->webrtc_api_startmedia(callunit->voice.chn, M_SEND, M_AUDIO);

  i = eXosip_call_build_answer(ctx, tid, 200, &answer);
  if (i != 0) {
    eXosip_call_send_answer(ctx, tid, 400, NULL);
  } else {
    // TODO: SDP

    osip_message_set_body(answer, tmp, strlen(tmp));
    osip_message_set_content_type(answer, "application/sdp");

    if (i == 0) {
      eXosip_call_send_answer(ctx, tid, 200, answer);
    }
  }
  eXosip_unlock(ctx);

  return 0;
}

int sip_hangup(int call_id) {
  // int call_id = 1;
  int dialog_id = call_id + 1;
  printf("call_id = %d dialog_id = %d\n", call_id, dialog_id);
  eXosip_lock(ctx);
  int i = eXosip_call_terminate(ctx, call_id, dialog_id);
  printf("sip_hangup ret = %d\n", i);

  closeMediaChannel(callunit, M_AUDIO);  // �����Ͽ�

  eXosip_unlock(ctx);
  return 0;
}

//=================== media =================
int startRender(int type, int callid) {
    return 0;
}

int stopRender(int type, int callid) {
    return 0;
}

/*Ĭ��ֻ���շ����ý����*/
int openMediaChannel(CallUnit* pcall, int type) {
  int iRet = 0;
   
  FUN_ENTRY
  CHECK_NULL_PTR(pcall);

  MediaUnit* voice = &pcall->voice;
  MediaUnit* video = &pcall->video;
  /*if (!MEDIA_INITED) {
    LOGE("engine not initialized");
    return -1;
  }*/
  // if(camera_permission == 0)
  // return -1;
  if (type == M_AUDIO) {
    ASS(voice->chn == -1);
    if (voice->chn == -1) {
#ifdef WEBRTC_IOS
      // IOS�����̨���Է��Ҷϴ�����ʱ��ϵͳ�̻߳�ֹͣ��
      if (IOS_STATE_Background == ios_state) {
        printf("IOS_STATE_Background==ios_state\r\n");
        pausesipsocket();
      }
#endif

      iRet = media->webrtc_api_createchannel(M_AUDIO, voice->chn);
      if (iRet < 0)
        return iRet;
      iRet = media->webrtc_api_set(voice->chn, AUD_DTMF);
      if (iRet < 0)
        return iRet;
      iRet = media->webrtc_api_set(voice->chn, AUD_PROC);
      if (iRet < 0)
        return iRet;
      iRet = media->webrtc_api_set(voice->chn, AUD_HARDW);
      if (iRet < 0)
        return iRet;
      iRet = media->webrtc_api_set(voice->chn, AUD_RTPRTCP);
      if (iRet < 0)
        return iRet;

      iRet = media->webrtc_api_createtrans(voice->chn, M_AUDIO, voice->trans,
                                           voice->localport);
      if (iRet < 0)
        return iRet;
      //strcpy(pcall->localvoiceip, sipua.localhostip);
      media->webrtc_api_startmedia(voice->chn, M_RECV, M_AUDIO);

#ifdef HAS_AUDIO_RECORD
      if (audiorec.autorecord) {
        CallUnit* reccall = getCallUnit(audiorec.recordcallid);
        if (reccall) {
          media->webrtc_api_record(M_START, reccall->achn, audiorec.nWavType,
                                   audiorec.szFile);
        }
      }
#endif
      // LOGI("achn=%d localvoiceport=%d", voice->chn, pcall->localvoiceport);
      // active_du->stat_period_secs = checkPeriodInSec;
    }
  } else if (type == M_VIDEO) {
    ASS(video->chn == -1);
    if (video->chn == -1) {
      //��һ����������Ƶͨ��
      media->webrtc_api_createchannel(M_VIDEO, video->chn);
      media->webrtc_api_set(video->chn, VID_NETW);
      media->webrtc_api_set(video->chn, VID_RTPRTCP, M_RECV);

      media->webrtc_api_createtrans(video->chn, M_VIDEO, video->trans,
                                    video->localport);
      //strcpy(pcall->localvideoip, sipua.localhostip);

      // LOGI("vchn=%d localvideoport=%d", video->chn,pcall->localvideoport);
      media->webrtc_api_startmedia(voice->chn, M_RECV, M_VIDEO);
      startRender(M_REMOTE, pcall->callid);
    }
  }

  FUN_EXIT

  return 0;
}

/*Ĭ�Ϲر��շ�˫���ý����*/
int closeMediaChannel(CallUnit* pcall, int type) {
  // LOG_INFO_LD(UALOG, "%s��ʼ", __FUNCTION__);
  FUN_ENTRY

  CHECK_NULL_PTR(pcall);
  if (!MEDIA_INITED) {
    //LOGE("engine not initialized");
    return -1;
  }

  MediaUnit* voice = &pcall->voice;
  MediaUnit* video = &pcall->video;

  if (type == M_AUDIO) {
    if (voice->chn == -1)
      return -1;

    media->webrtc_api_stopmedia(voice->chn, M_DUAL, M_AUDIO);
#ifdef WEBRTC_IOS
    // jumping.z����IOS�����̨���Է��Ҷϴ�����ʱ��ϵͳ�̻߳�ֹͣ��
    if (IOS_STATE_Background == ios_state) {
      printf("IOS_STATE_Background==ios_state\r\n");
      pausesipsocket();
    }
#endif

#ifdef HAS_AUDIO_RECORD
    media->webrtc_api_record(M_STOP, voice->chn);
    audiorec.autorecord = false;
    audiorec.recordcallid = -1;
    memset(audiorec.szFile, 0, sizeof(audiorec.szFile));
#endif

    media->webrtc_api_deletetrans(M_AUDIO, voice->trans,
                                  voice->localport);
    media->webrtc_api_deletechannel(M_AUDIO, voice->chn);

  } else if (type == M_VIDEO) {
    if (video->chn == -1)
      return -1;

    stopRender(M_REMOTE, pcall->callid);
    media->webrtc_api_stopmedia(video->chn, M_DUAL, M_VIDEO);

    media->webrtc_api_camera(CAMERA_DISCONNECT, caminfo->captureId, (void*)&video->chn);
    // media->webrtc_api_render(M_STOP, video->chn);
    media->webrtc_api_deletetrans(M_VIDEO, video->trans,
                                  video->localport);
    media->webrtc_api_deletechannel(M_VIDEO, video->chn);

  }
  FUN_EXIT

  return 0;
}
